#pragma once
#include "Sock.hpp"
#include "Log.hpp"
#include "Util.hpp"
#include "Epoll.hpp"
#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>
#include <cassert>
#include "Protocol.hpp"

namespace reactor
{
    // 类的申明
    class TcpServer;
    class Connection;

    const static uint16_t defaultPort = 8888;
    const static uint16_t num = 64;
    using func_t = std::function<void(Connection *)>;
    class Connection
    {
    public:
        Connection(int sock , TcpServer *tcp )
            : _sock(sock), _tsp(tcp)
        {
        }

        void Register(func_t recver , func_t sender , func_t excepter )
        {
            this->_recver = recver;
            this->_sender = sender;
            this->_excepter = excepter;
        }
        void Close()
        {
            close(_sock);
        }

    public:
        int _sock;
        std::string _inbuffer;
        std::string _outbuffer;
        // 定义三个函数指针
        func_t _recver;
        func_t _sender;
        func_t _excepter;
        TcpServer *_tsp;
    };
    class TcpServer
    {
    private:
        void recver(Connection *conn)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t s = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;
                    conn->_inbuffer += buffer;
                    logMessage(DEBUG, "\n%s", conn->_inbuffer.c_str());
                    _service(conn);
                }
                else if (s == 0)
                {
                    if (conn->_excepter)
                    {
                        conn->_excepter(conn);
                        return;
                    }
                }
                else
                {
                    // 分为几种情况
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        break;
                    }
                    else if (errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return;
                        }
                    }
                }
            }
        }
        void sender(Connection *conn)
        {
            logMessage(DEBUG, "sender in");
            while (true)
            {
                ssize_t s = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
                if (s > 0)
                {
                    if (conn->_outbuffer.empty())
                    {
                        // std::cout<<222<<std::endl;
                        break;
                    }
                    else
                    {
                        conn->_outbuffer.erase(0, s);
                    }
                }
                else if(s==0)
                {
                    break;
                }
                else
                {
                    std::cout<<errno<<strerror(errno)<<std::endl;
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        break;
                    }
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return;
                        }
                    }
                }
            }
            if (!conn->_outbuffer.empty())
                conn->_tsp->EnableReadWrite(conn, true, true);
            else
                conn->_tsp->EnableReadWrite(conn, true, false);

            logMessage(DEBUG, "sender out");
        }

        void excepter(Connection *conn)
        {
            logMessage(DEBUG, "Excepter begin");
            _epoller.control(conn->_sock, 0, EPOLL_CTL_DEL);
            conn->Close();
            _connections.erase(conn->_sock);

            logMessage(DEBUG, "关闭%d 文件描述符的所有的资源", conn->_sock);

            delete conn;
            logMessage(DEBUG, "Excepter out");
        }

        void accepter(Connection *conn)
        {
            // 外面这层死循环是为了让服务器一直去监听这个连接。有一个时间如果同时来了很多请求的话，这样能处理完
            for (;;)
            {
                std::string clientip;
                uint16_t clientport;
                int err = 0;
                // sock小于0在这个函数里面就会被处理掉，还有等于0的情况
                // 问题一：
                int sock = _sock.Accept(&clientip, &clientport, &err);
                if (sock > 0)
                {
                    // 这样写看看行不行
                    AddConnection(sock, EPOLLIN | EPOLLET,
                                  std::bind(&TcpServer::recver, this, std::placeholders::_1),
                                  std::bind(&TcpServer::sender, this, std::placeholders::_1),
                                  std::bind(&TcpServer::excepter, this, std::placeholders::_1));
                    logMessage(DEBUG, "get a new link,info:[%s:%d]", clientip.c_str(), clientport);
                }
                else
                {
                    // std::cout<<strerror(err)<<err<<std::endl;
                    if (err == EAGAIN || err == EWOULDBLOCK)
                    {
                        // 这里不是错误，只是没有数据
                        // std::cout << 11111111111111 << std::endl;
                        break;
                    }
                    else if (err == EINTR)
                        // 系统的打断，后面继续处理
                        continue;
                    else
                        // 真正的错误
                        break;
                }
            }
        }

        void AddConnection(int sock, uint32_t events, func_t recver, func_t sender, func_t excepter)
        {
            // 1. 首先要为该sock创建Connection，并初始化，并添加到connections_
            // cao       capcao oocoacocoaocaoc o
            if (events & EPOLLET)
            {
                Util::SetNonBlock(sock);
            }
            Connection *conn = new Connection(sock, this);
            conn->Register(recver, sender, excepter);
            bool r = _epoller.addEvent(sock, events);
            assert(r);
            (void)r;
            _connections.insert(std::pair<int, Connection *>(sock, conn));
            logMessage(DEBUG, "add new sock : %d in epoll and unordered_map", sock);
        }
        bool IsConnectionExists(int sock)
        {
            auto iter = _connections.find(sock);
            return iter != _connections.end();
        }
        void Loop(int time)
        {
            int n = _epoller.Wait(_revs, _num, time);

            for (int i = 0; i < n; i++)
            {
                int sock = _revs[i].data.fd;
                uint32_t events = _revs[i].events;
                // 将所有的异常问题，全部转化 成为读写问题
                if (events & EPOLLERR)
                {
                    events |= (EPOLLIN | EPOLLOUT);
                }
                // 另外一端断开连接
                if (events & EPOLLHUP)
                {
                    events |= (EPOLLIN | EPOLLOUT);
                }
                // listen 事件就绪 读事件
                if ((events & EPOLLIN) && IsConnectionExists(sock) && _connections[sock]->_recver)
                {
                    _connections[sock]->_recver(_connections[sock]);
                }
                // 些事件就绪
                if ((events & EPOLLOUT) && (IsConnectionExists(sock)) && _connections[sock]->_sender)
                {
                    _connections[sock]->_sender(_connections[sock]);
                }
            }
        }

    public:
        TcpServer(uint16_t port = defaultPort, func_t func = nullptr, int num = num)
            : _port(port), _revs(nullptr), _num(num), _service(func)
        {
        }
        void InitServer()
        {
            // 创建套接字
            _sock.Socket();
            // 绑定套接字
            _sock.Bind(_port);
            // 设置为监听状态
            _sock.Listen();

            // 由于ET模式 ，我们需要分拣描述符是需要设置为非阻塞的，为什么呢？
            // ET模式通知你读取数据只有在数据变化的时候才会通知你 -》 因为他高效，所以我们为了他这种高效而做出改变，我们要发送过来的数据一次性读完，
            // LT模式则是你有数据就会一直通知你
            // 发送过来的数据为什么要一次性读完呢？-》怎么读完-》循环读取，一直读取。-》这时候就有一个新的问题-》什么时候读完了
            // 阻塞IO只会在没有数据的时候阻塞在哪里
            // 非阻塞IO则是有3个返回值
            // >0 读取到好多数据
            // =0 返回值等于 0，说明已经到达文件末尾，没有更多的数据可读
            // <0 错误 又分为3种，看后面的代码演示
            // Util::SetNonBlock(_sock);
            // 接着就是要加入epoll模型 ,先创建

            _epoller.create();
            // 接着就是把listensock放入epoll模型中 ，封装成为一个函数。不仅要添加事件，同时还需要把fd添加到我们的组织中
            // _epoller.addEvent(_sock, EPOLLIN);
            AddConnection(_sock.getFd(), EPOLLIN | EPOLLET, std::bind(&TcpServer::accepter, this, std::placeholders::_1), nullptr, nullptr);
            _revs = new struct epoll_event[_num];
        }

        void EnableReadWrite(Connection *conn, bool readable, bool writeable)
        {
            uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
            _epoller.control(conn->_sock, event, EPOLL_CTL_MOD);
        }
        // -------------------------------------------------------------------------

        //  -----------------------------

        void Dispatcher()
        {
            // 时间单位ms
            int timeout = 1000;
            while (true)
            {
                Loop(timeout); // 这里直接写wait也是一样的 ，是会一直监听的
                // logMessage(DEBUG, "time out ...");

                // 遍历connections_，计算每一个链接的已经有多长时间没有动了
            }
        }

        ~TcpServer()
        {
            _sock.Close();
            _epoller.Close();
            if (nullptr != _revs)
                delete[] _revs;
        }

    private:
        uint16_t _port;
        Sock _sock;
        Epoll _epoller;
        // wait 需要两个参数
        struct epoll_event *_revs;
        int _num;
        std::unordered_map<int, Connection *> _connections; // 先描述再组织
        func_t _service;
    };
}